<?php
/**
 * @author		João Batista Neto
 * @brief		Objetos que representam cada tipo do retorno da API
 * @package		dso.buscape.result.type
 */

require_once 'dso/buscape/result/type/BuscapeComposite.php';

/**
 * @ingroup		Types
 * @brief		Categoria de produtos BuscaPé
 * @details		Existem três tipos de categorias:
 * @li	<b>Categoria Raiz</b>	- É o primeiro nível da árvore. Através dela obtemos
 * as subcategorias. (BuscapeComposite)
 * @li	<b>Subcategoria</b>	- É o segundo nível da árvore, em que podemos obter
 * outras subcategorias ou categorias finais. (BuscapeComposite)
 * @li	<b>Categoria Final</b>	- É o último nível da árvore, estando relacionado a
 * uma lista de produtos, ofertas ou serviços. (BuscapeLeaf).<br /><br />
 * @dot
  * digraph CategoryComposite {
 * 	size="8,10";
 * 	compound=true;
 *
 * 	subgraph Categories {
 * 		root [ label="Categoria Raiz", style=filled, color="#FF9900" ];
 *
 * 		subgraph SubCategories {
 * 			schild1		[ label="Joalheria",				style=filled, color="#99CDFB" ];
 *			schild2		[ label="Telefonia",				style=filled, color="#99CDFB" ];
 *
 *			subgraph SubSucCategories {
 *				schild21	[ label="Smartphone",			style=filled, color="#9B98F6" ];
 *				schild22	[ label="Cellphone",			style=filled, color="#9B98F6" ];
 *				schild23	[ label="Downloads",			style=filled, color="#9B98F6" ];
 *
 *				subgraph FinalCategories {
 *					schild12	[ label="Relógio de Pulso",	style=filled, color="#019904" ];
 *					schild211	[ label="Produto1",			style=filled, color="#019904" ];
 *					schild212	[ label="Produto2",			style=filled, color="#019904" ];
 *					schild213	[ label="Produto3",			style=filled, color="#019904" ];
 *
 *					schild221	[ label="Produto1",			style=filled, color="#019904" ];
 *
 *					schild231	[ label="RingTone",			style=filled, color="#019904" ];
 *					schild232	[ label="Wallpaper",		style=filled, color="#019904" ];
 *				}
 *			}
 *		}
 *
 *		fchild12	[ label="Ofertas",			style=filled, color="#AF4911" ];
 *		fchild211	[ label="Ofertas",			style=filled, color="#AF4911" ];
 *		fchild212	[ label="Ofertas",			style=filled, color="#AF4911" ];
 *		fchild213	[ label="Ofertas",			style=filled, color="#AF4911" ];
 *		fchild221	[ label="Ofertas",			style=filled, color="#AF4911" ];
 *		fchild231	[ label="Ofertas",			style=filled, color="#AF4911" ];
 *		fchild232	[ label="Ofertas",			style=filled, color="#AF4911" ];
 *
 *	 	root -> schild1;
 *	 	schild1 -> schild12;
 *	 	schild12 -> fchild12;
 *	 	root -> schild2;
 *	 	schild2 -> schild21;
 *	 	schild2 -> schild22;
 *	 	schild2 -> schild23;
 *		schild21 -> schild211;
 *		schild21 -> schild212;
 *		schild21 -> schild213;
 *		schild22 -> schild221;
 *		schild23 -> schild231;
 *		schild23 -> schild232;
 *		schild211 -> fchild211;
 *		schild212 -> fchild212;
 *		schild213 -> fchild213;
 *		schild221 -> fchild221;
 *		schild231 -> fchild231;
 *		schild232 -> fchild232;
 *	}
 * }
 * @enddot
 * <br /><br />
 * @section Structure Estruturalmente:
 * <br /><br />
 * @dot
 * digraph Composite {
 *		node [shape=record];
 *		dir=none;
 *		layout=neato;
 *
 *		Client [
 *		    pos="0,3.44",
 *		    pin="true"
 *		];
 *
 *		Component [
 *		    pos="3,3",
 *		    pin="true",
 *		    label="{Categoria|+ add( Categoria )\l+ remove( Categoria )\l+ operation()\l}",
 *		    style=filled,
 *		    fillcolor="#FF9900"
 *		];
 *
 *		Leaf [
 *		    pos="1.5,1.4",
 *		    pin="true",
 *		    label="{Categoria Final|+ operation()\l}",
 *		    style=filled,
 *		    fillcolor="#019904"
 *		];
 *
 *		Composite [
 *		    pos="4.5,1",
 *		    pin="true",
 *		    label="{Sub Categoria|- children :Categoria [1..*]\l|+ add( Categoria )\l+ remove( Categoria )\l+ operation()\l}",
 *		    style=filled,
 *		    fillcolor="#9B98F6"
 *		];
 *
 *		LeafT [
 *		    width=0.01,
 *		    height=0.01,
 *		    pos="1.5,2",
 *		    pin="true",
 *		    shape="point",
 *		    label=""
 *		];
 *
 *		CompositeT [
 *		    width=0.01,
 *		    height=0.01,
 *		    pos="4.5,2",
 *		    pin="true",
 *		    shape="point",
 *		    label=""
 *		];
 *
 *		LeafComposite [
 *		    width=0.01,
 *		    height=0.01,
 *		    pos="3,2",
 *		    pin="true",
 *		    shape="point",
 *		    label=""
 *		];
 *
 *		CompositeRB [
 *		    width=0.01,
 *		    height=0.01,
 *		    pos="6.5,1.26",
 *		    pin="true",
 *		    shape="point",
 *		    label=""
 *		];
 *
 *		CompositeRT [
 *		    width=0.01,
 *		    height=0.01,
 *		    pos="6.5,3.44",
 *		    pin="true",
 *		    shape="point",
 *		    label=""
 *		];
 *
 *		Code [
 *		    pos="7.7,0.36",
 *		    pin="true",
 *		    label="foreach ( children as child )\nchild.operation();",
 *			style="filled",
 *			fillcolor="#FAFFB3",
 *		];
 *
 *		Point0 [
 *		    pos="2.03,3.44",
 *		    pin="true",
 *		    width="0.01",
 *		    height="0.01",
 *		    shape="point",
 *		    label=""
 *		];
 *
 *		Point1 [
 *		    pos="3.97,3.44",
 *		    pin="true",
 *		    width="0.01",
 *		    height="0.01",
 *		    label="",
 *		    shape="point",
 *		    style="filled",
 *		    fillcolor="#000000"
 *		];
 *
 *		Point2 [
 *			width="0.01",
 *			height="0.01",
 *			shape="point",
 *			pos="5.63,1.26",
 *			pin="true",
 *			label=""
 *		];
 *
 *		Point3 [
 *		    width="0.1",
 *		    height="0.1",
 *		    pos="4.6,0.36",
 *		    pin="true",
 *		    shape="circle",
 *		    style="filled",
 *		    fillcolor="#ffffff",
 *		    label=""
 *		];
 *
 *		Client -> Point0;
 *
 *		Leaf -> LeafT [arrowhead="none"];
 *		Composite -> CompositeT [arrowhead="none"];
 *		LeafT -> LeafComposite -> CompositeT [arrowhead="none"];
 *		LeafComposite -> Component [arrowhead="empty"];
 *
 *		CompositeRB -> Point2 [arrowhead="odiamond",label="children",fontsize="6"];
 *		CompositeRB -> CompositeRT [arrowhead="none"];
 *		CompositeRT -> Point1;
 *
 *		Point3 -> Code [arrowhead="none",style="dashed"];
 * }
 * @enddot
 * <p>Veja a página sobre o serviço de busca de categorias para maiores informações: @ref CategoryListPage</p>
 * @class		BuscapeCategory
 * @defgroup	Category Categorias de Produtos
 */
class BuscapeCategory extends BuscapeComposite {
	/**
	 * @brief	ID da categoria
	 * @var		string
	 */
	private $id;

	/**
	 * @brief	Identifica uma categoria final
	 * @var		boolean
	 */
	private $isFinal;

	/**
	 * @brief	Indica se a categoria possui ofertas
	 * @var		boolean
	 */
	private $hasOffer;

	/**
	 * @brief	Recupera a lista de links da categoria
	 * @var		ArrayObject
	 */
	private $links;

	/**
	 * @brief	Nome da categoria
	 * @var		string
	 */
	private $name;

	/**
	 * @brief	Thumbnail da categoria
	 * @var		Thumbnail
	 */
	private $thumbnail;

	/**
	 * @brief	Identifica uma categoria que está na lista de Top5
	 * @var		boolean
	 */
	private $top5Category = false;

	/**
	 * @brief	Verifica se um filho é aceitável pelo componente
	 * @param	$child BuscapeComponent
	 * @return	boolean
	 * @see		BuscapeComponent.accept()
	 */
	protected function accept( IObject $child ){
		if ( parent::accept( $child ) ){
			return $child->getClass()->isSubclassOf( 'BuscapeType' );
		}

		return false;
	}

	/**
	 * @brief	Adiciona um novo link à categoria.
	 * @param	$link BuscapeLink O link que será adicionado.
	 * @return	BuscapeCategory Esta categoria
	 */
	public function addLink( BuscapeLink $link ){
		if ( is_null( $this->links ) ){
			$this->links = new RecursiveComponentIterator();
		}

		$this->links->append( $link );

		return $this;
	}

	/**
	 * @brief	Cria uma instância do objeto com os argumentos passados
	 * @param	$args array Lista de argumentos que serão utilizados para criação do objeto
	 * @return	BuscapeCategory
	 */
	public static function createInstante( array $args ){
		$category = new BuscapeCategory();

		foreach ( $args as $property => $value ){
			$category->$property = $value;
		}

		return $category;
	}

	/**
	 * @brief	Recupera o ID da categoria
	 * @return	integer O ID da categoria
	 */
	public function getId(){
		return $this->id;
	}

	/**
	 * @brief	Recupera um iterator para os links
	 * @return	RecursiveComponentIterator
	 * @throws	BadMethodCallException Se não houverem links
	 */
	public function getLinks(){
		if ( !is_null( $this->links ) ){
			return $this->links->getIterator();
		} else {
			throw new BadMethodCallException( 'Não existem links para serem retornados.' );
		}
	}

	/**
	 * @brief	Recupera o nome da categoria
	 * @return	string
	 */
	public function getName(){
		return $this->name;
	}

	/**
	 * @brief	Recupera a categoria superior à essa categoria
	 * @return	BuscapeCategory
	 * @see		BuscapeComponent.getFather(), BuscapeType.hasFather()
	 * @throws	RuntimeException Se não for possível recuperar a categoria anterior
	 */
	public function getParentCategory(){
		try {
			return $this->getFather();
		} catch ( BadMethodCallException $e ){
			throw new RuntimeException( 'Não foi possível recuperar a categoria anterior' , null , $e );
		}
	}

	/**
	 * @brief	Recupera o ID da categoria anterior
	 * @return	integer
	 */
	public function getParentCategoryId(){
		return $this->getParentCategory()->getId();
	}

	/**
	 * @brief	Verifica se a categoria possui ofertas
	 * @return	boolean
	 */
	public function hasOffer(){
		return (bool) $this->hasOffer;
	}

	/**
	 * @brief	Verifica se a categoria é final
	 * @return	boolean
	 */
	public function isFinal(){
		return $this->isFinal;
	}

	/**
	 * @brief	Verifica se a categoria é Tpo 5
	 * @return	boolean
	 */
	public function isTop5Category(){
		return $this->top5Category;
	}
}